package com.mdd.front;

import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.mdd.common.helper.HashAlgorithms;
import com.mdd.common.util.RedisUtils;
import com.mdd.common.util.RequestUtils;
import com.mdd.front.validate.commons.BufferedServletRequestWrapper;
import com.mdd.front.vo.annotation.PreventRepeatSubmit;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @author xuliduo
 * @date 2018/12/19
 * @description 通用防重复提交拦截器
 */
@Component
@Log4j2
public class RepeatedInterceptor extends HandlerInterceptorAdapter {
    private final static String key = "REPEATED_";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            PreventRepeatSubmit annotation = method.getAnnotation(PreventRepeatSubmit.class);
            if (annotation != null && annotation.check()) {
                BufferedServletRequestWrapper requestWrapper = new BufferedServletRequestWrapper(request);
                String token = getRequestHash(requestWrapper);
                if(!RedisUtils.setIfAbsent(key + token, 1, annotation.time(), TimeUnit.SECONDS)) {
                    throw new Exception();
                }
            }
        }
        return super.preHandle(request, response, handler);
    }

    /**
     * 通过request uri、querystring和body 计算出一个hash key
     *
     * @param request HttpServletRequest
     *
     * @return hash string
     */
    private String getRequestHash(HttpServletRequest request) throws IOException, NullPointerException {
        String token = request.getRequestURI();
        String requestMethod = request.getMethod();
        token += getRequestHeaderStr(request);
        if (StringUtils.isNotEmpty(request.getQueryString())) {
            token += request.getQueryString();
        }
        if ("POST".equals(requestMethod) || "PUT".equals(requestMethod)) {
            token += getRequestPostStr(request);
        }
        log.debug("token->{}", token);
        return String.valueOf(HashAlgorithms.FNVHash1(token));
    }

    /**
     * 通过request获得request body
     *
     * @param request HttpServletRequest
     *
     * @return body byte array
     *
     * @throws IOException getInput read error
     */
    private byte[] getRequestPostBytes(HttpServletRequest request)
            throws IOException {
        int contentLength = request.getContentLength();
        if (contentLength < 0) {
            return null;
        }
        byte[] buffer = new byte[contentLength];
        for (int i = 0; i < contentLength; ) {

            int readlen = request.getInputStream().read(buffer, i,
                    contentLength - i);
            if (readlen == -1) {
                break;
            }
            i += readlen;
        }
        return buffer;

    }

    /**
     * 获得request body的 字符串
     *
     * @param request HttpServletRequest
     *
     * @return body String
     *
     * @throws IOException when error
     */
    private String getRequestPostStr(HttpServletRequest request)
            throws IOException, NullPointerException {
        byte[] buffer = getRequestPostBytes(request);
        String charEncoding = request.getCharacterEncoding();
        if (charEncoding == null) {
            charEncoding = "UTF-8";
        }
        assert buffer != null;
        return new String(buffer, charEncoding);
    }

    /**
     * 获得 request header的 字符串
     *
     * @param request HttpServletRequest
     *
     * @return header
     */
    private String getRequestHeaderStr(HttpServletRequest request) {
        StringBuilder message = new StringBuilder("[ ");
        RequestUtils.getRequestHeaderStr(request, message);
        message.append("]");
        return message.toString();
    }

}
